import json
import time
from threading import Thread
from channels.generic.websocket import WebsocketConsumer
from AutoOperation.settings import AutoOperationExecutorManager
from tools.executorTools import Executor
from tools.publicConf import CollectorExecutor, ProducerExecutor, PublisherExecutor


class ConsoleWebsocket(WebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super(ConsoleWebsocket, self).__init__(*args, **kwargs)
        self.connectStatus = False

    # websocket建立连接时执行方法
    def connect(self):
        self.accept()
        self.connectStatus = True
        # 启动AllExecutorInfo推送
        Thread(target=self.sendAllExecutorInfo, daemon=True).start()

    # websocket断开时执行方法
    def disconnect(self, close_code):
        self.close()
        self.connectStatus = False

    # websocket发送消息的重写方法
    def send(self, text_data=None, bytes_data=None, close=False):
        if self.connectStatus:
            super(ConsoleWebsocket, self).send(text_data=text_data, bytes_data=bytes_data, close=close)

    def sendAllExecutorInfo(self):
        oldMessage = []
        while self.connectStatus:
            message = []
            for executor in AutoOperationExecutorManager.executorPool:
                executorInfo = {
                    "executorName": executor.executorName,
                    "executorBaseName": executor.executorBaseName,
                    "schedulerJobTime": AutoOperationExecutorManager.getExecutorSchedulerJobTime(executor),
                    "executorLastStartTime": executor.lastStartTime,
                    "executorRunningTaskNum": executor.taskPoolSize(),
                    "executorTotalNum": getExecutorTotalNum(executor),
                    "executorStatus": {
                        "executorIsRunning": AutoOperationExecutorManager.executorIsRunning(executor),
                        "blockPutTaskStatus": executor.blockPutTaskStatus
                    }
                }
                message.append(executorInfo)
            if oldMessage != message:
                print(message)
                self.send(
                    text_data=json.dumps({"type": "getAllExecutorInfo", "message": message},
                                         ensure_ascii=False))
            oldMessage = message
            time.sleep(0.5)

    # 从websocket接收到消息时执行函数
    def receive(self, text_data=None, bytes_data=None):
        data = json.loads(text_data)
        data_type = data["type"]
        message = None

        # 暂停executor
        if data_type == "pauseExecutor":
            executor = AutoOperationExecutorManager.findExecutor(executorName=data["executorName"])
            executor.blockPutTask()
            message = {"status": True}

        # 继续运行executor
        elif data_type == "startUpExecutor":
            executor = AutoOperationExecutorManager.findExecutor(executorName=data["executorName"])
            executor.allowPutTask()
            message = {"status": True}

        # 启动executor
        elif data_type == "startExecutor":
            executor = AutoOperationExecutorManager.findExecutor(executorName=data["executorName"])
            AutoOperationExecutorManager.runExecutor(executor)
            message = {"status": True}

        # 增加定时任务
        elif data_type == "addSchedulerJob":
            executor = AutoOperationExecutorManager.findExecutor(executorName=data["executorName"])
            hour = data["hour"]
            minute = data["minute"]
            AutoOperationExecutorManager.addExecutorScheduler(executor=executor, hour=hour, minute=minute)
            message = {"status": True}

        # 取消定时任务
        elif data_type == "removeSchedulerJob":
            executor = AutoOperationExecutorManager.findExecutor(executorName=data["executorName"])
            AutoOperationExecutorManager.removeExecutorScheduler(executor=executor)
            message = {"status": True}

        # app需要发布的model
        elif data_type == "PublisherExecutorSelector":
            executor = AutoOperationExecutorManager.findExecutor(executorName=data["publisherExecutorName"])
            selectedExecutors = data["selectedExecutors"]
            models = []
            for selectedExecutorName in selectedExecutors:
                models.append(AutoOperationExecutorManager.findExecutor(executorName=selectedExecutorName).model)
            executor.models = models
            message = {"status": True}

        # 获取最新所有app需要发布的models
        elif data_type == "getAllPublisherExecutorSelectedModels":
            allPublisherExecutorSelectedModels = {}
            for publisherExecutor in [executor for executor in AutoOperationExecutorManager.executorPool if
                                      issubclass(executor.__class__, PublisherExecutor)]:
                allPublisherExecutorSelectedModels[publisherExecutor.executorName] = []
                for model in publisherExecutor.models:
                    for producerExecutor in [executor for executor in AutoOperationExecutorManager.executorPool if
                                             issubclass(executor.__class__, ProducerExecutor)]:
                        if producerExecutor.model == model:
                            allPublisherExecutorSelectedModels[publisherExecutor.executorName].append(
                                producerExecutor.executorName)
                            break
            message = {"allPublisherExecutorSelectedModels": allPublisherExecutorSelectedModels}
        self.send(text_data=json.dumps({"type": data_type, "message": message}, ensure_ascii=False))


def getExecutorTotalNum(executor):
    """
    得到executor的库存数量
    @param executor: Executor的实例对象
    @return: 数量
    """

    if not isinstance(executor, Executor):
        raise TypeError("executor应为Executor实例对象")

    if issubclass(executor.__class__, CollectorExecutor):
        return executor.model.objects.count()

    if issubclass(executor.__class__, ProducerExecutor):
        return executor.model.objects.filter(videoPath__isnull=False).count()

    if issubclass(executor.__class__, PublisherExecutor):
        num = 0
        for producerExecutor in [executor for executor in AutoOperationExecutorManager.executorPool if
                                 issubclass(executor.__class__, ProducerExecutor)]:
            num += producerExecutor.model.objects.filter(usedApps__contains=executor.__class__.__name__).count()
        return num
    return None
